﻿using System;
using System.Collections.Generic;
using System.Data;
using l1cs.server.model;
using l1cs.server.model.Instance;
using l1cs.server.templates;
using MySql.Data.MySqlClient;
using l1cs.server.utils;

namespace l1cs.server.datatables
{
    public class ItemTable
    {
        private static Logger _log = Logger.getLogger( typeof( ItemTable ).FullName );

        private static readonly Dictionary<String, int> _armorTypes = new Dictionary<String, int>();

        private static readonly Dictionary<String, int> _weaponTypes = new Dictionary<String, int>();

        private static readonly Dictionary<String, int> _weaponId = new Dictionary<String, int>();

        private static readonly Dictionary<String, int> _materialTypes = new Dictionary<String, int>();

        private static readonly Dictionary<String, int> _etcItemTypes = new Dictionary<String, int>();

        private static readonly Dictionary<String, int> _useTypes = new Dictionary<String, int>();

        private static ItemTable _instance;

        private L1Item[] _allTemplates;

        private readonly Dictionary<int, L1EtcItem> _etcitems;

        private readonly Dictionary<int, L1Armor> _armors;

        private readonly Dictionary<int, L1Weapon> _weapons;

        static ItemTable()
        {
            _etcItemTypes["arrow"] = 0;
            _etcItemTypes["wand"] = 1;
            _etcItemTypes["light"] = 2;
            _etcItemTypes["gem"] = 3;
            _etcItemTypes["totem"] = 4;
            _etcItemTypes["firecracker"] = 5;
            _etcItemTypes["potion"] = 6;
            _etcItemTypes["food"] = 7;
            _etcItemTypes["scroll"] = 8;
            _etcItemTypes["questitem"] = 9;
            _etcItemTypes["spellbook"] = 10;
            _etcItemTypes["petitem"] = 11;
            _etcItemTypes["other"] = 12;
            _etcItemTypes["material"] = 13;
            _etcItemTypes["event"] = 14;
            _etcItemTypes["sting"] = 15;
            _etcItemTypes["treasure_box"] = 16;

            _useTypes["none"] = -1; // 使用不可能
            _useTypes["normal"] = 0;
            _useTypes["weapon"] = 1;
            _useTypes["armor"] = 2;
            // ワンドを振るアクションをとる(C_RequestExtraCommandが送られる)
            _useTypes["spell_long"] = 5; // 地面 / オブジェクト選択(遠距離)
            _useTypes["ntele"] = 6;
            _useTypes["identify"] = 7;
            _useTypes["res"] = 8;
            _useTypes["letter"] = 12;
            _useTypes["letter_w"] = 13;
            _useTypes["choice"] = 14;
            _useTypes["instrument"] = 15;
            _useTypes["sosc"] = 16;
            _useTypes["spell_short"] = 17; // 地面 / オブジェクト選択(近距離)
            _useTypes["T"] = 18;
            _useTypes["cloak"] = 19;
            _useTypes["glove"] = 20;
            _useTypes["boots"] = 21;
            _useTypes["helm"] = 22;
            _useTypes["ring"] = 23;
            _useTypes["amulet"] = 24;
            _useTypes["shield"] = 25;
            _useTypes["guarder"] = 25;
            _useTypes["dai"] = 26;
            _useTypes["zel"] = 27;
            _useTypes["blank"] = 28;
            _useTypes["btele"] = 29;
            _useTypes["spell_buff"] = 30; // オブジェクト選択(遠距離)
            // Ctrlを押さないとパケットが飛ばない？
            _useTypes["ccard"] = 31;
            _useTypes["ccard_w"] = 32;
            _useTypes["vcard"] = 33;
            _useTypes["vcard_w"] = 34;
            _useTypes["wcard"] = 35;
            _useTypes["wcard_w"] = 36;
            _useTypes["belt"] = 37;
            // _useTypes["spell_long2"] = 39; // 地面 / オブジェクト選択(遠距離)
            // 5と同じ？
            _useTypes["earring"] = 40;
            _useTypes["fishing_rod"] = 42;
            _useTypes["del"] = 46;

            _armorTypes["none"] = 0;
            _armorTypes["helm"] = 1;
            _armorTypes["armor"] = 2;
            _armorTypes["T"] = 3;
            _armorTypes["cloak"] = 4;
            _armorTypes["glove"] = 5;
            _armorTypes["boots"] = 6;
            _armorTypes["shield"] = 7;
            _armorTypes["amulet"] = 8;
            _armorTypes["ring"] = 9;
            _armorTypes["belt"] = 10;
            _armorTypes["ring2"] = 11;
            _armorTypes["earring"] = 12;
            _armorTypes["guarder"] = 13;

            _weaponTypes["sword"] = 1;
            _weaponTypes["dagger"] = 2;
            _weaponTypes["tohandsword"] = 3;
            _weaponTypes["bow"] = 4;
            _weaponTypes["spear"] = 5;
            _weaponTypes["blunt"] = 6;
            _weaponTypes["staff"] = 7;
            _weaponTypes["throwingknife"] = 8;
            _weaponTypes["arrow"] = 9;
            _weaponTypes["gauntlet"] = 10;
            _weaponTypes["claw"] = 11;
            _weaponTypes["edoryu"] = 12;
            _weaponTypes["singlebow"] = 13;
            _weaponTypes["singlespear"] = 14;
            _weaponTypes["tohandblunt"] = 15;
            _weaponTypes["tohandstaff"] = 16;
            _weaponTypes["kiringku"] = 17;
            _weaponTypes["chainsword"] = 18;

            _weaponId["sword"] = 4;
            _weaponId["dagger"] = 46;
            _weaponId["tohandsword"] = 50;
            _weaponId["bow"] = 20;
            _weaponId["blunt"] = 11;
            _weaponId["spear"] = 24;
            _weaponId["staff"] = 40;
            _weaponId["throwingknife"] = 2922;
            _weaponId["arrow"] = 66;
            _weaponId["gauntlet"] = 62;
            _weaponId["claw"] = 58;
            _weaponId["edoryu"] = 54;
            _weaponId["singlebow"] = 20;
            _weaponId["singlespear"] = 24;
            _weaponId["tohandblunt"] = 11;
            _weaponId["tohandstaff"] = 40;
            _weaponId["kiringku"] = 58;
            _weaponId["chainsword"] = 24;

            _materialTypes["none"] = 0;
            _materialTypes["liquid"] = 1;
            _materialTypes["web"] = 2;
            _materialTypes["vegetation"] = 3;
            _materialTypes["animalmatter"] = 4;
            _materialTypes["paper"] = 5;
            _materialTypes["cloth"] = 6;
            _materialTypes["leather"] = 7;
            _materialTypes["wood"] = 8;
            _materialTypes["bone"] = 9;
            _materialTypes["dragonscale"] = 10;
            _materialTypes["iron"] = 11;
            _materialTypes["steel"] = 12;
            _materialTypes["copper"] = 13;
            _materialTypes["silver"] = 14;
            _materialTypes["gold"] = 15;
            _materialTypes["platinum"] = 16;
            _materialTypes["mithril"] = 17;
            _materialTypes["blackmithril"] = 18;
            _materialTypes["glass"] = 19;
            _materialTypes["gemstone"] = 20;
            _materialTypes["mineral"] = 21;
            _materialTypes["oriharukon"] = 22;
        }

        public static ItemTable getInstance()
        {
            if ( _instance == null )
            {
                _instance = new ItemTable();
            }
            return _instance;
        }

        private ItemTable()
        {/*TTOK*/
            _etcitems = allEtcItem();
            _weapons = allWeapon();
            _armors = allArmor();
            buildFastLookupTable();
        }

        private Dictionary<int, L1EtcItem> allEtcItem()
        {/*TTOK*/
            Dictionary<int, L1EtcItem> result = new Dictionary<int, L1EtcItem>();
            L1EtcItem etcitem = null;
            String sqlstr = "select * from etcitem";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    etcitem = new L1EtcItem();
                    etcitem.setItemId( SqlUtils.toInt( item["item_id"] ) );
                    etcitem.setName( Convert.ToString( item["name"] ) );
                    etcitem.setUnidentifiedNameId( Convert.ToString( item["unidentified_name_id"] ) );
                    etcitem.setIdentifiedNameId( Convert.ToString( item["identified_name_id"] ) );
                    etcitem.setType( _etcItemTypes[Convert.ToString( item["item_type"] )] );
                    etcitem.setUseType( _useTypes[Convert.ToString( item["use_type"] )] );
                    etcitem.setType2( 0 );
                    etcitem.setMaterial( _materialTypes[Convert.ToString( item["material"] )] );
                    etcitem.setWeight( SqlUtils.toInt( item["weight"] ) );
                    etcitem.setGfxId( SqlUtils.toInt( item["invgfx"] ) );
                    etcitem.setGroundGfxId( SqlUtils.toInt( item["grdgfx"] ) );
                    etcitem.setItemDescId( SqlUtils.toInt( item["itemdesc_id"] ) );
                    etcitem.setMinLevel( SqlUtils.toInt( item["min_lvl"] ) );
                    etcitem.setMaxLevel( SqlUtils.toInt( item["max_lvl"] ) );
                    etcitem.setBless( SqlUtils.toInt( item["bless"] ) );
                    etcitem.setTradable( SqlUtils.toInt( item["trade"] ) == 0 ? true : false );
                    etcitem.setCantDelete( SqlUtils.toInt( item["cant_delete"] ) == 1 ? true : false );
                    etcitem.setCanSeal( SqlUtils.toInt( item["can_seal"] ) == 1 ? true : false );
                    etcitem.setDmgSmall( SqlUtils.toInt( item["dmg_small"] ) );
                    etcitem.setDmgLarge( SqlUtils.toInt( item["dmg_large"] ) );
                    etcitem.set_stackable( SqlUtils.toInt( item["stackable"] ) == 1 ? true : false );
                    etcitem.setMaxChargeCount( SqlUtils.toInt( item["max_charge_count"] ) );
                    etcitem.set_locx( SqlUtils.toInt( item["locx"] ) );
                    etcitem.set_locy( SqlUtils.toInt( item["locy"] ) );
                    etcitem.set_mapid( SqlUtils.toShort( item["mapid"] ) );
                    etcitem.set_delayid( SqlUtils.toInt( item["delay_id"] ) );
                    etcitem.set_delaytime( SqlUtils.toInt( item["delay_time"] ) );
                    etcitem.set_delayEffect( SqlUtils.toInt( item["delay_effect"] ) );
                    etcitem.setFoodVolume( SqlUtils.toInt( item["food_volume"] ) );
                    etcitem.setToBeSavedAtOnce( ( SqlUtils.toInt( item["save_at_once"] ) == 1 ) ? true : false );

                    result[etcitem.getItemId()] = etcitem;
                }
            }
            return result;
        }

        private Dictionary<int, L1Weapon> allWeapon()
        {/*TTOK*/
            Dictionary<int, L1Weapon> result = new Dictionary<int, L1Weapon>();
            L1Weapon weapon = null;
            String sqlstr = "select * from weapon";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    weapon = new L1Weapon();
                    weapon.setItemId( SqlUtils.toInt( item["item_id"] ) );
                    weapon.setName( Convert.ToString( item["name"] ) );
                    weapon.setUnidentifiedNameId( Convert.ToString( item["unidentified_name_id"] ) );
                    weapon.setIdentifiedNameId( Convert.ToString( item["identified_name_id"] ) );
                    weapon.setType( _weaponTypes[Convert.ToString( item["type"] )] );
                    weapon.setType1( _weaponId[Convert.ToString( item["type"] )] );
                    weapon.setType2( 1 );
                    weapon.setUseType( 1 );
                    weapon.setMaterial( _materialTypes[Convert.ToString( item["material"] )] );
                    weapon.setWeight( SqlUtils.toInt( item["weight"] ) );
                    weapon.setGfxId( SqlUtils.toInt( item["invgfx"] ) );
                    weapon.setGroundGfxId( SqlUtils.toInt( item["grdgfx"] ) );
                    weapon.setItemDescId( SqlUtils.toInt( item["itemdesc_id"] ) );
                    weapon.setDmgSmall( SqlUtils.toInt( item["dmg_small"] ) );
                    weapon.setDmgLarge( SqlUtils.toInt( item["dmg_large"] ) );
                    weapon.setRange( SqlUtils.toInt( item["range"] ) );
                    weapon.set_safeenchant( SqlUtils.toInt( item["safenchant"] ) );
                    weapon.setUseRoyal( SqlUtils.toInt( item["use_royal"] ) == 0 ? false : true );
                    weapon.setUseKnight( SqlUtils.toInt( item["use_knight"] ) == 0 ? false : true );
                    weapon.setUseElf( SqlUtils.toInt( item["use_elf"] ) == 0 ? false : true );
                    weapon.setUseMage( SqlUtils.toInt( item["use_mage"] ) == 0 ? false : true );
                    weapon.setUseDarkelf( SqlUtils.toInt( item["use_darkelf"] ) == 0 ? false : true );
                    weapon.setUseDragonknight( SqlUtils.toInt( item["use_dragonknight"] ) == 0 ? false : true );
                    weapon.setUseIllusionist( SqlUtils.toInt( item["use_illusionist"] ) == 0 ? false : true );
                    weapon.setHitModifier( SqlUtils.toInt( item["hitmodifier"] ) );
                    weapon.setDmgModifier( SqlUtils.toInt( item["dmgmodifier"] ) );
                    weapon.set_addstr( Convert.ToSByte( item["add_str"] ) );
                    weapon.set_adddex( Convert.ToSByte( item["add_dex"] ) );
                    weapon.set_addcon( Convert.ToSByte( item["add_con"] ) );
                    weapon.set_addint( Convert.ToSByte( item["add_int"] ) );
                    weapon.set_addwis( Convert.ToSByte( item["add_wis"] ) );
                    weapon.set_addcha( Convert.ToSByte( item["add_cha"] ) );
                    weapon.set_addhp( SqlUtils.toInt( item["add_hp"] ) );
                    weapon.set_addmp( SqlUtils.toInt( item["add_mp"] ) );
                    weapon.set_addhpr( SqlUtils.toInt( item["add_hpr"] ) );
                    weapon.set_addmpr( SqlUtils.toInt( item["add_mpr"] ) );
                    weapon.set_addsp( SqlUtils.toInt( item["add_sp"] ) );
                    weapon.set_mdef( SqlUtils.toInt( item["m_def"] ) );
                    weapon.setDoubleDmgChance( SqlUtils.toInt( item["double_dmg_chance"] ) );
                    weapon.setMagicDmgModifier( SqlUtils.toInt( item["magicdmgmodifier"] ) );
                    weapon.set_canbedmg( SqlUtils.toInt( item["canbedmg"] ) );
                    weapon.setMinLevel( SqlUtils.toInt( item["min_lvl"] ) );
                    weapon.setMaxLevel( SqlUtils.toInt( item["max_lvl"] ) );
                    weapon.setBless( SqlUtils.toInt( item["bless"] ) );
                    weapon.setTradable( !SqlUtils.toBool( item["trade"] ) );
                    weapon.setCantDelete( SqlUtils.toBool( item["cant_delete"] ) );
                    weapon.setHasteItem( SqlUtils.toBool( item["haste_item"] ) );
                    weapon.setMaxUseTime( SqlUtils.toInt( item["max_use_time"] ) );

                    result[weapon.getItemId()] = weapon;
                }
            }
            return result;
        }

        private Dictionary<int, L1Armor> allArmor()
        {/*TTOK*/
            Dictionary<int, L1Armor> result = new Dictionary<int, L1Armor>();
            L1Armor armor = null;
            String sqlstr = "select * from armor";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    armor = new L1Armor();
                    armor.setItemId( SqlUtils.toInt( item["item_id"] ) );
                    armor.setName( Convert.ToString( item["name"] ) );
                    armor.setUnidentifiedNameId( Convert.ToString( item["unidentified_name_id"] ) );
                    armor.setIdentifiedNameId( Convert.ToString( item["identified_name_id"] ) );
                    armor.setType( _armorTypes[Convert.ToString( item["type"] )] );
                    armor.setType2( 2 );
                    armor.setUseType( _useTypes[Convert.ToString( item["type"] )] );
                    armor.setMaterial( _materialTypes[Convert.ToString( item["material"] )] );
                    armor.setWeight( SqlUtils.toInt( item["weight"] ) );
                    armor.setGfxId( SqlUtils.toInt( item["invgfx"] ) );
                    armor.setGroundGfxId( SqlUtils.toInt( item["grdgfx"] ) );
                    armor.setItemDescId( SqlUtils.toInt( item["itemdesc_id"] ) );
                    armor.set_ac( SqlUtils.toInt( item["ac"] ) );
                    armor.set_safeenchant( SqlUtils.toInt( item["safenchant"] ) );
                    armor.setUseRoyal( SqlUtils.toBool( item["use_royal"] ) );
                    armor.setUseKnight( SqlUtils.toBool( item["use_knight"] ) );
                    armor.setUseElf( SqlUtils.toBool( item["use_elf"] ) );
                    armor.setUseMage( SqlUtils.toBool( item["use_mage"] ) );
                    armor.setUseDarkelf( SqlUtils.toBool( item["use_darkelf"] ) );
                    armor.setUseDragonknight( SqlUtils.toBool( item["use_dragonknight"] ) );
                    armor.setUseIllusionist( SqlUtils.toBool( item["use_illusionist"] ) );
                    armor.set_addstr( Convert.ToSByte( item["add_str"] ) );
                    armor.set_addcon( Convert.ToSByte( item["add_con"] ) );
                    armor.set_adddex( Convert.ToSByte( item["add_dex"] ) );
                    armor.set_addint( Convert.ToSByte( item["add_int"] ) );
                    armor.set_addwis( Convert.ToSByte( item["add_wis"] ) );
                    armor.set_addcha( Convert.ToSByte( item["add_cha"] ) );
                    armor.set_addhp( SqlUtils.toInt( item["add_hp"] ) );
                    armor.set_addmp( SqlUtils.toInt( item["add_mp"] ) );
                    armor.set_addhpr( SqlUtils.toInt( item["add_hpr"] ) );
                    armor.set_addmpr( SqlUtils.toInt( item["add_mpr"] ) );
                    armor.set_addsp( SqlUtils.toInt( item["add_sp"] ) );
                    armor.setMinLevel( SqlUtils.toInt( item["min_lvl"] ) );
                    armor.setMaxLevel( SqlUtils.toInt( item["max_lvl"] ) );
                    armor.set_mdef( SqlUtils.toInt( item["m_def"] ) );
                    armor.setDamageReduction( SqlUtils.toInt( item["damage_reduction"] ) );
                    armor.setWeightReduction( SqlUtils.toInt( item["weight_reduction"] ) );
                    armor.setHitModifierByArmor( SqlUtils.toInt( item["hit_modifier"] ) );
                    armor.setDmgModifierByArmor( SqlUtils.toInt( item["dmg_modifier"] ) );
                    armor.setBowHitModifierByArmor( SqlUtils.toInt( item["bow_hit_modifier"] ) );
                    armor.setBowDmgModifierByArmor( SqlUtils.toInt( item["bow_dmg_modifier"] ) );
                    armor.setHasteItem( SqlUtils.toBool( item["haste_item"] ) );
                    armor.setBless( SqlUtils.toInt( item["bless"] ) );
                    armor.setTradable( SqlUtils.toBool( item["trade"] ) );
                    armor.setCantDelete( SqlUtils.toBool( item["cant_delete"] ) );
                    armor.set_defense_earth( SqlUtils.toInt( item["defense_earth"] ) );
                    armor.set_defense_water( SqlUtils.toInt( item["defense_water"] ) );
                    armor.set_defense_wind( SqlUtils.toInt( item["defense_wind"] ) );
                    armor.set_defense_fire( SqlUtils.toInt( item["defense_fire"] ) );
                    armor.set_regist_stun( SqlUtils.toInt( item["regist_stun"] ) );
                    armor.set_regist_stone( SqlUtils.toInt( item["regist_stone"] ) );
                    armor.set_regist_sleep( SqlUtils.toInt( item["regist_sleep"] ) );
                    armor.set_regist_freeze( SqlUtils.toInt( item["regist_freeze"] ) );
                    armor.set_regist_sustain( SqlUtils.toInt( item["regist_sustain"] ) );
                    armor.set_regist_blind( SqlUtils.toInt( item["regist_blind"] ) );
                    armor.setMaxUseTime( SqlUtils.toInt( item["max_use_time"] ) );

                    result[armor.getItemId()] = armor;
                }
            }
            return result;
        }

        /// <summary>
        /// 全アイテムを一元管理する配列を作成する。
        /// </summary>
        private void buildFastLookupTable()
        {/*TTOK*/
            int highestId = 0;

            foreach ( L1EtcItem item in _etcitems.Values )
            {
                if ( item.getItemId() > highestId )
                {
                    highestId = item.getItemId();
                }
            }

            foreach ( L1Weapon weapon in _weapons.Values )
            {
                if ( weapon.getItemId() > highestId )
                {
                    highestId = weapon.getItemId();
                }
            }

            foreach ( L1Armor armor in _armors.Values )
            {
                if ( armor.getItemId() > highestId )
                {
                    highestId = armor.getItemId();
                }
            }

            _allTemplates = new L1Item[highestId + 1];

            foreach ( L1EtcItem item in _etcitems.Values )
            {
                _allTemplates[item.getItemId()] = item;
            }

            foreach ( L1Weapon item in _weapons.Values )
            {
                _allTemplates[item.getItemId()] = item;
            }

            foreach ( L1Armor item in _armors.Values )
            {
                _allTemplates[item.getItemId()] = item;
            }
        }

        /// <summary>
        /// アイテムIDからアイテム情報を返す。
        /// </summary>
        /// <param name="id">アイテムID</param>
        /// <returns>L1Item</returns>
        public L1Item getTemplate( int id )
        {/*TTOK*/
            if ( _allTemplates.Length > id )
            {
                return _allTemplates[id];
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// アイテムをDBから呼び出しアイテム生成する。(0だと未ヒット)
        /// </summary>
        /// <param name="itemId">アイテムID</param>
        /// <returns>アイテムインスタンス</returns>
        public L1ItemInstance createItem( int itemId )
        {/*TTOK*/
            L1Item temp = getTemplate( itemId );
            if ( temp == null )
            {
                return null;
            }
            L1ItemInstance item = new L1ItemInstance();
            item.setId( IdFactory.getInstance().nextId() );
            item.setItem( temp );
            L1World.getInstance().storeObject( item );
            return item;
        }

        /// <summary>
        /// アイテム名からアイテムIDを探して返す。(0だと未ヒット)
        /// </summary>
        /// <param name="name">アイテム名</param>
        /// <returns>アイテムID</returns>
        public int findItemIdByName( String name )
        {/*TTOK*/
            int itemid = 0;
            foreach ( L1Item item in _allTemplates )
            {
                if ( item != null && item.getName().Equals( name ) )
                {
                    itemid = item.getItemId();
                    break;
                }
            }
            return itemid;
        }

        /// <summary>
        /// 空白を除去したアイテム名からアイテムIDを探して返す。
        /// </summary>
        /// <param name="name">アイテム名</param>
        /// <returns>アイテムID</returns>
        public int findItemIdByNameWithoutSpace( String name )
        {/*TTOK*/
            int itemid = 0;
            foreach ( L1Item item in _allTemplates )
            {
                if ( item != null && item.getName().Replace( " ", "" ).Equals( name ) )
                {
                    itemid = item.getItemId();
                    break;
                }
            }
            return itemid;
        }
    }
}
